package jnpf.database.util;

import jnpf.database.constant.DbConst;
import jnpf.database.enums.DbAliasEnum;
import jnpf.database.model.DbFieldMod;
import jnpf.database.model.JdbcPageMod;
import jnpf.database.model.dto.JdbcDTO;
import jnpf.database.model.dto.ModelDTO;
import jnpf.database.model.interfaces.JdbcGetMod;
import jnpf.database.source.DbBase;
import jnpf.database.sql.SqlBase;
import jnpf.exception.DataException;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * jdbc自定义工具类
 *
 * @author JNPF开发平台组
 * @version V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date 2021/3/16 8:49
 */
@Slf4j
public class JdbcUtil {

    /**
     * 批量执行sql语句(适合增、删、改)
     * CRUD：增加(Create)、检索(Retrieve)、更新(Update)、删除(Delete)
     * 输入的语句需要全小写，除了数据
     *
     * @param conn 数据连接
     * @param sql  sql语句
     */
    public static int creUpDe(Connection conn, String sql) throws DataException {
        try {
            @Cleanup PreparedStatement preparedStatement = null;
            creUpDeRollback(conn, sql, false);
            //批量提交事务
            conn.commit();
            return 1;
            //捕捉回滚操作
        } catch (SQLSyntaxErrorException s) {
            throw DataException.tableExists(s.getMessage(), conn);
        } catch (Exception e) {
            throw DataException.rollbackDataException(e.getMessage(), conn);
        }
    }

    /**
     * 可回滚
     * 注意：执行完，必须手动执行 conn.commit()
     */
    public static void creUpDeRollback(Connection conn, String sql, Boolean autoCommitFlag) throws SQLException {
        PreparedStatement preparedStatement = null;
        try {
            conn.setAutoCommit(autoCommitFlag);
            //事务自动提交（默认true，即自动提交事务）
            //注意：表引擎不为InnoDB，回滚失败，MySQL中，DDL创建Create、删除Drop和更改Alter表结构等操作回滚无效。
            for (String sqlOne : sql.split(";")) {
                preparedStatement = conn.prepareStatement(sqlOne);
                preparedStatement.executeUpdate();
            }
        } finally {
            close(conn, preparedStatement, null);
        }
    }

    /*========================query查询语句==============================*/

    /**
     * 通用：多条查询
     */
    public static List<Map<String, Object>> queryList(Connection conn, String sql) throws DataException {
        JdbcDTO<JdbcGetMod> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.MAP_MOD);
        return query(sql, conn, jdbcDTO).getMapMods();
    }

    /**
     * 通用：多条查询（查询别名）
     */
    public static List<Map<String, Object>> queryListAlias(Connection conn,String sql)throws DataException{
        JdbcDTO<JdbcGetMod> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.MAP_MOD);
        jdbcDTO.setAliasFlag(true);
        return query(sql, conn, jdbcDTO).getMapMods();
    }

    /**
     * 通用：多条查询（开启小写）
     */
    public static List<Map<String, Object>> queryListLowercase(Connection conn, String sql) throws DataException {
        JdbcDTO<JdbcGetMod> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.MAP_MOD);
        //开启小写模式
        jdbcDTO.setLowercaseFlag(true);
        return query(sql, conn, jdbcDTO).getMapMods();
    }

    /**
     * 通用：单条查询
     */
    public static Map<String, Object> queryOne(Connection conn, String sql) throws DataException {
        List<Map<String, Object>> mapList = queryList(conn, sql);
        if (mapList.size() > 0) {
            return queryList(conn, sql).get(0);
        } else {
            return new HashMap<>();
        }
    }

    /**
     * 查单条Int类型返回值
     */
    public static Integer queryOneInt(Connection conn, String sql, String keyWord) throws DataException {
        List<Map<String, Object>> mapList = queryList(conn, sql);
        if (mapList.size() > 0) {
            Map<String, Object> map = queryOne(conn, sql);
            System.out.println(map);
            keyWord = DbAliasEnum.getAsByDb(DbTypeUtil.getDb(conn), keyWord);
            return Integer.parseInt(queryOne(conn, sql).get(keyWord).toString());
        } else {
            throw new DataException("未查到信息");
        }
    }

    /**
     * 专用：查询模板
     * 说明：DbJdbcModel对象，为通用的数据返回对象，每条信息不同字段对应的数据，包含此相应字段的信息
     */
    public static List<List<DbFieldMod>> queryIncludeFieldMods(Connection conn, String sql) throws DataException {
        JdbcDTO<JdbcGetMod> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.INCLUDE_FIELD_MOD);
        return query(sql, conn, jdbcDTO).getIncludeFieldMods();
    }

    /**
     * 专用：打印模板使用
     */
    public static List<List<DbFieldMod>> queryTableFields(Connection conn, String sql) throws DataException {
        JdbcDTO<JdbcGetMod> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.TABLE_FIELD_MOD);
        return query(sql, conn, jdbcDTO).getTableFieldMods();
    }

    /**
     * 自定义模板查询
     */
    public static <T extends JdbcGetMod> List<T> queryCustomMods(Connection conn, String sql, Class<T> modType) throws DataException {
        JdbcDTO<T> jdbcDTO = new JdbcDTO<>();
        jdbcDTO.setReturnType(DbConst.CUSTOM_MOD);
        jdbcDTO.setModType(modType);
        jdbcDTO.setDbBase(DbTypeUtil.getDb(conn));
        return query(sql, conn, jdbcDTO).getCustomMods();
    }

    public static JdbcPageMod queryPage(Connection conn, String sql, String sortType, Integer currentPage, Integer pageSize) throws DataException {
        DbBase db = DbTypeUtil.getDb(conn);
        SqlBase sqlBase = db.getSqlBase();
        String[] sqlArray = sqlBase.getPageSql(sql, sortType, currentPage, pageSize);
        List<Map<String, Object>> mapList = queryListAlias(conn, sqlArray[0]);
        JdbcPageMod<Map<String, Object>> pageModel = new JdbcPageMod<>();
        pageModel.setDataList(mapList);
        pageModel.setTotalRecord(queryOneInt(conn, sqlArray[1], DbAliasEnum.TOTAL_RECORD.asByDb(db)));
        pageModel.setCurrentPage(currentPage);
        pageModel.setPageSize(pageSize);
        return pageModel;
    }

    /*=====================================================*/
    /**
     * Jdbc查询
     *
     * @param prepareSql 执行的sql语句
     * @param jdbcDTO    所需相关参数
     * @param <T>        自定义对象模型类型
     * @return 查询结果
     * @throws DataException ignore
     */
    private static <T extends JdbcGetMod> JdbcDTO<T> query(String prepareSql, Connection conn, JdbcDTO<T> jdbcDTO) throws DataException {
        PreparedStatement pps = null;
        ResultSet result = null;
        try {
            if (conn != null) {
                pps = conn.prepareStatement(prepareSql);
            }
            if (pps != null) {
                result = pps.executeQuery();
            }
            //对ResultSet进行转换
            if (result != null) {
                return getList(result, jdbcDTO);
            } else {
                throw new DataException("查询结果集为空");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new DataException(e.getMessage());
        } finally {
            close(conn, pps, result);
        }
    }

    private static void close(Connection conn, PreparedStatement pps, ResultSet result) {
        //关闭ResultSet
        try {
            if (result != null) {
                result.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭PreparedStatement
            try {
                if (pps != null) {
                    pps.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static <T extends JdbcGetMod> JdbcDTO<T> getList(ResultSet result, JdbcDTO<T> jdbcDTO) throws Exception {
        //自定义jdbc模型对象
        List<T> customMods = new ArrayList<>();
        List<Map<String, Object>> mapMods = new ArrayList<>();
        List<List<DbFieldMod>> includeFieldMods = new ArrayList<>();
        List<List<DbFieldMod>> tableFieldMods = new ArrayList<>();
        //给这些集合赋值
        if (DbConst.MAP_MOD.equals(jdbcDTO.getReturnType())) {
            while (result.next()) {
                mapMods.add(getModel0(result, jdbcDTO));
            }
        } else if (DbConst.INCLUDE_FIELD_MOD.equals(jdbcDTO.getReturnType())) {
            while (result.next()) {
                includeFieldMods.add(getModel1(result, jdbcDTO));
            }
        } else if (DbConst.TABLE_FIELD_MOD.equals(jdbcDTO.getReturnType())) {
            //DbJdbcModel集合保底为1条数据，为了返回字段相关信息
            tableFieldMods.add(getModel3(result, jdbcDTO));
        } else if (DbConst.CUSTOM_MOD.equals(jdbcDTO.getReturnType())) {
            while (result.next()) {
                T newT = jdbcDTO.getModType().newInstance();
                customMods.add(getModel2(new ModelDTO(result, jdbcDTO.getDbBase()), newT));
            }
        }
        //返回值：自定义jdbc模型对象
        jdbcDTO.setCustomMods(customMods);
        //返回值：List<Map<String, Object>>
        jdbcDTO.setMapMods(mapMods);
        //返回值：包含字段模型
        jdbcDTO.setIncludeFieldMods(includeFieldMods);
        //返回值：返回表字段信息
        jdbcDTO.setTableFieldMods(tableFieldMods);
        return jdbcDTO;
    }


    /**
     * ResultSet转Map
     *
     * @param jdbcDTO 所需相关参数
     * @param <T>     未使用
     * @return 结果集的Map集合
     * @throws SQLException ignore
     */
    private static <T> Map<String, Object> getModel0(ResultSet rs, JdbcDTO<T> jdbcDTO) throws SQLException {
        ResultSetMetaData md = rs.getMetaData();
        Map<String, Object> map = new HashMap<>();
        //获取字段集合信息
        int columnCount = md.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
            String fieldName = jdbcDTO.getAliasFlag() ? md.getColumnLabel(i) : md.getColumnName(i);
            fieldName = jdbcDTO.getLowercaseFlag() ? fieldName.toLowerCase() : fieldName;
            map.put(fieldName, rs.getString(i));
        }
        return map;
    }

    /**
     * 基本对象模型 （包含类型）
     *
     * @param jdbcDTO 所需相关参数
     * @param <T>     未使用
     * @return 包含字段信息的结果集对象
     * @throws SQLException ignore
     */
    private static <T> List<DbFieldMod> getModel1(ResultSet rs, JdbcDTO<T> jdbcDTO) throws SQLException {
        ResultSetMetaData md = rs.getMetaData();
        int columnCount = md.getColumnCount();
        List<DbFieldMod> list = new ArrayList<>();
        for (int i = 1; i <= columnCount; i++) {
            list.add(new DbFieldMod(md.getTableName(i), md.getColumnLabel(i), md.getColumnName(i), md.getColumnTypeName(i),
                    md.getColumnType(i), rs.getString(i), jdbcDTO.getLowercaseFlag()));
        }
        return list;
    }

    /**
     * 获取自定义对象模型集合
     *
     * @param t   自定义对象模型一个实例
     * @param <T> 自定义对象模型类型
     * @return 自定义对象集合
     * @throws SQLException ignore
     */
    private static <T extends JdbcGetMod> T getModel2(ModelDTO modelDTO, T t) throws SQLException {
        t.setMod(modelDTO);
        return t;
    }

    /**
     * 获取表字段信息
     *
     * @param jdbcDTO 所需相关参数
     * @param <T>     未使用
     * @return 返回字段信息
     * @throws SQLException ignore
     */
    private static <T> List<DbFieldMod> getModel3(ResultSet rs, JdbcDTO<T> jdbcDTO) throws SQLException {
        ResultSetMetaData md = rs.getMetaData();
        List<DbFieldMod> dbFieldModList = new ArrayList<>();
        for (int i = 1; i <= md.getColumnCount(); i++) {
            dbFieldModList.add(new DbFieldMod(md.getTableName(i), md.getColumnLabel(i), md.getColumnName(i), md.getColumnTypeName(i),
                    md.getColumnType(i), null, jdbcDTO.getLowercaseFlag()));
        }
        return dbFieldModList;
    }

    /**
     * 分页处理
     *
     * @param rs
     * @param jdbcDTO
     * @param <T>
     * @return
     * @throws SQLException
     */
    private static <T> Map<String, Object> getModel4(ResultSet rs, JdbcDTO<T> jdbcDTO) throws SQLException {
        ResultSetMetaData md = rs.getMetaData();
        Map<String, Object> map = new HashMap<>();
        //获取字段集合信息
        int columnCount = md.getColumnCount();
        for (int i = 1; i <= columnCount; i++) {
            String fieldName = jdbcDTO.getLowercaseFlag() ? md.getColumnName(i).toLowerCase() : md.getColumnName(i);
            map.put(fieldName, rs.getString(i));
        }
        return map;
    }
}
